<?php
/**
 * helper class containing methods for generating, parsing and validating a nonce.
 * (nonce == A unique number that is used once)
 *
 * $Id: nonce.php 2008-09-25 17:28:07 BST Atomless $
 *
 * @package    Openid
 * @author     Kohana Team
 * @copyright  (c) 2007-2008 Kohana Team
 * @license    http://kohanaphp.com/license.html
 */
class nonce_Core {

	const TIME_FORMAT = '%Y-%m-%dT%H:%M:%SZ';

	const REGEX = '/(\d{4})-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)Z(.*)/';

	/**
	 * Create a NONCE
	 *
	 * @param  string  time string to use in creating the nonce
	 * @return string  unique nonce string
	 */
	public static function create($when = FALSE)
	{
		$salt = text::random('alnum',6);

		$when = ($when === FALSE)? time() : $when;

		$time_string = gmstrftime(nonce::TIME_FORMAT, $when);

		return $time_string.$salt;
	}

	/**
	 * Check a nonce has not expired
	 *
	 * @param  string   nonce string
	 * @param  string   valid nonce lifetime in seconds - defaults to max_association_lifetime value in config/openid.php
	 * @param  string   time to check validity against defaults to that generated by php's time() method
	 * @return boolean
	 */
	public static function is_alive($nonce_string, $allowed_lifetime = FALSE, $now = FALSE)
	{
		$allowed_lifetime = ($allowed_lifetime === FALSE)
						  ? KOHANA::config('openid.max_association_lifetime')
						  : $allowed_lifetime;

		$now = ($now === FALSE)? time() : $now;

		$parts = nonce::split($nonce_string);

		if (! is_array($parts))
			return false;

		$nonce_time_stamp = $parts[0];

		$time_stamp_of_oldest_valid_nonce = $now - $allowed_lifetime;

		$time_stamp_of_youngest_valid_nonce = $now + $allowed_lifetime;

		// is the stamp not too far in the future and not too far in the past?
		return (($time_stamp_of_oldest_valid_nonce <= $nonce_time_stamp) AND ($nonce_time_stamp <= $time_stamp_of_youngest_valid_nonce));
	}

	/**
	 * Split a nonce into it's constituent timestamp and salt parts - used when extracting a timestamp from
	 * a given nonce string
	 *
	 * @param  string   nonce string
	 * @return mixed    array(timestamp, salt) or FALSE on failure
	 */
	public static function split($nonce_string)
	{
		if (nonce::valid($nonce_string) !== TRUE)
			return FALSE;

		preg_match(nonce::REGEX, $nonce_string, $matches);

		list
		(
			$unused,
			$year,
			$month,
			$day,
			$hour,
			$minute,
			$second,
			$salt
		) = $matches;

		$timestamp = @gmmktime($hour, $minute, $second, $month, $day, $year);

		if ($timestamp === false OR $timestamp < 0)
			return FALSE;

		return array($timestamp, $salt);
	}

	/**
	 * Validate a nonce
	 *
	 * @param  string  nonce string
	 * @return boolean
	 */
	public static function valid($nonce_string)
	{
		if (strlen($nonce_string) >= 255)
			return FALSE;

		$result = preg_match(nonce::REGEX, $nonce_string, $matches);

		if ($result != 1 OR count($matches) != 8)
			return FALSE;

		return TRUE;
	}
}